﻿using System;
using System.Diagnostics;
using System.Linq;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
using DevExpress.Xpf.Core;
using HelixToolkit.Wpf.SharpDX;
using MoreLinq;
using SharpDX;
using WindLoad;
using Camera = HelixToolkit.Wpf.SharpDX.Camera;
using Color = System.Windows.Media.Color;
using OrthographicCamera = HelixToolkit.Wpf.SharpDX.OrthographicCamera;
using Point3D = System.Windows.Media.Media3D.Point3D;
using Vector3D = System.Windows.Media.Media3D.Vector3D;

namespace Media3DTest
{
    public partial class MainWindow : ThemedWindow
    {
        public MainWindow()
        {
            InitializeComponent();

            //
            // effect manager
            //
            EffectsManager = new DefaultEffectsManager();

            //
            // camera setup
            //
            Camera = new OrthographicCamera
            {
                Position = new Point3D(0, -100, 0),
                LookDirection = new Vector3D(0, 100, 0),
                UpDirection = new Vector3D(0, 0, 1),
                FarPlaneDistance = 5000000
            };

            //
            // setup lighting   
            //
            AmbientLightColor = Colors.DimGray;
            DirectionalLightColor = Colors.White;
            DirectionalLightDirection = new Vector3D(-2, -5, -2);

            //
            DataContext = this;
        }

        public EffectsManager EffectsManager { get; private set; }
        public Camera Camera { get; private set; }

        public Vector3D DirectionalLightDirection { get; private set; }
        public Color DirectionalLightColor { get; private set; }
        public Color AmbientLightColor { get; private set; }

        public Vector3D UpDirection { set; get; } = new Vector3D(0, 0, 1);

        private readonly MctModel _mctModel = new MctModel();

        private LineGeometry3D _sineCurveGeom = new LineGeometry3D() {IsDynamic = true};

        private void MenuItem_OnClick(object sender, RoutedEventArgs e)
        {
            var mctFilePath = @"F:\云盘\大桥院\马鞍山大桥\建模\20191229\07-风荷载.mct";
            _mctModel.ReadMctFileData(mctFilePath);
            Debug.Assert(_mctModel.ElemDict.Count > 0);

            var nodeDict = _mctModel.NodeDict;
            var elemDict = _mctModel.ElemDict;
            var groupDict = _mctModel.GroupDict;

            //
            ListBoxGroupNames.ItemsSource = groupDict.Select(g => g.Key);
            ListBoxGroupNames.SelectedIndex = -1;

            //
            // Elems
            //
            var builder = new LineBuilder();
            elemDict.ForEach(el =>
            {
                var elem = el.Value;
                var node1 = nodeDict[elem.NodeIds[0]];
                var node2 = nodeDict[elem.NodeIds[1]];
                builder.AddLine(node1.ToVector3(), node2.ToVector3());
            });
            ElemModel.Geometry = builder.ToLineGeometry3D();

            //
            // Nodes
            //
            var vects = new Vector3Collection(nodeDict.Select(nd => nd.Value.ToVector3()));
            var pointGeom = new PointGeometry3D
            {
                Positions = vects,
                Indices = new IntCollection(Enumerable.Range(0, vects.Count))
            };
            NodeModel.Geometry = pointGeom;

            //
            // Node Id
            //
            var text = new BillboardText3D();
            nodeDict.ForEach(nd =>
            {
                text.TextInfo.Add(new TextInfo
                {
                    Text = $"{nd.Value.Id}",
                    Origin = nd.Value.ToVector3() + new Vector3(0, 0, 0.2f),
                    Foreground = Color4.White,
                    Scale = 0.5f,
                });
            });
            NodeIdModel.Geometry = text;

            //
            // Load symbol
            //
            builder = new LineBuilder();
            builder.AddLine(Vector3.Zero, new Vector3(10, 10, -20));
            LoadSymbolModel.Geometry = builder.ToLineGeometry3D();
            LoadSymbolModel.Material = new LineArrowHeadMaterial
            {
                ArrowSize = 0.2, Color = Colors.Green, Thickness = 0.6,
                FixedSize = true, EnableDistanceFading = false, TextureScale = 0.5,
            };

            //
            // Sine curve
            //
            SineCurve.Geometry = _sineCurveGeom;
            UpdateSineCurve();
        }

        //private void MainView_OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        //{
        //    var viewport = sender as Viewport3DX;
        //    Debug.Assert(viewport != null);

        //    var point = e.GetPosition(viewport);
        //    var hitTests = viewport.FindHits(point);
        //    if (hitTests == null || hitTests.Count <= 0)
        //        return;

        //    var hit = hitTests[0];
        //    if (hit.ModelHit is LineGeometryModel3D)
        //    {
        //        var model = (LineGeometryModel3D) hit.ModelHit;
        //        Debug.Assert(model != null);
        //        model.Color = Colors.Green;
        //    }
        //}

        private void LineModel_OnMouse3DDown(object sender, MouseDown3DEventArgs e)
        {
            Debug.Assert(e.OriginalInputEventArgs is MouseButtonEventArgs);
            if (((MouseButtonEventArgs) e.OriginalInputEventArgs).LeftButton != MouseButtonState.Pressed)
                return;

            Debug.Assert(e.HitTestResult is LineHitTestResult);
            var index = ((LineHitTestResult) e.HitTestResult).LineIndex;

            var nodeDict = _mctModel.NodeDict;
            var elemDict = _mctModel.ElemDict;
            var elem = elemDict.ElementAt(index).Value;
            var builder = new LineBuilder();
            var node1 = nodeDict[elem.NodeIds[0]];
            var node2 = nodeDict[elem.NodeIds[1]];
            builder.AddLine(node1.ToVector3(), node2.ToVector3());

            SelectedLineModel.Geometry = builder.ToLineGeometry3D();
            SelectedLineModel.IsRendering = true;
        }

        private void ListBoxGroupNames_OnSelectedIndexChanged(object sender, RoutedEventArgs e)
        {
            SelectedLineModel.IsRendering = false;

            var index = ListBoxGroupNames.SelectedIndex;
            if (index < 0 && SelectedLineModel.Geometry != null)
            {
                SelectedLineModel.Geometry = null;
                return;
            }

            SelectedLineModel.Geometry = null;

            var nodeDict = _mctModel.NodeDict;
            var elemDict = _mctModel.ElemDict;
            var groupDict = _mctModel.GroupDict;

            var builder = new LineBuilder();
            groupDict.ElementAt(index).Value.ElemIds
                .ForEach(id =>
                {
                    var elem = elemDict[id];
                    var node1 = nodeDict[elem.NodeIds[0]];
                    var node2 = nodeDict[elem.NodeIds[1]];
                    builder.AddLine(node1.ToVector3(), node2.ToVector3());
                });
            SelectedLineModel.Geometry = builder.ToLineGeometry3D();
            SelectedLineModel.IsRendering = true;
        }

        private void MenuItemShowNode_OnChecked(object sender, RoutedEventArgs e)
        {
            if (NodeModel != null)
                NodeModel.IsRendering = MenuItemShowNode.IsChecked;
        }

        private void MenuItemShowNodeId_OnChecked(object sender, RoutedEventArgs e)
        {
            if (NodeIdModel != null)
                NodeIdModel.IsRendering = MenuItemShowNodeId.IsChecked;
        }

        private void MainView_OnKeyDown(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.Escape)
            {
                SelectedLineModel.IsRendering = false;
                SelectedLineModel.Geometry = null;
            }
        }

        private Random _random = new Random();

        private void UpdateSineCurve()
        {
            var randomColors = new[]
            {
                Colors.Magenta, Colors.Wheat, Colors.Cyan
            };
            var pointCount = 100;

            var intList = Enumerable.Range(0, pointCount).ToList();
            var positions = new Vector3Collection(intList.Select(i => new Vector3(i, 0, _random.Next(0, 10))));
            var indices = new IntCollection(intList);
            var colors = new Color4Collection(Enumerable.Range(0, pointCount / 2).SelectMany(i =>
            {
                var color = randomColors[_random.Next(0, randomColors.Length)];
                var color4 = new Color4(new Color3(color.R / 255f, color.G / 255f, color.B / 255f), 1);
                return new[] {color4, color4};
            }));
            _sineCurveGeom.Positions = positions;
            _sineCurveGeom.Indices = indices;
            _sineCurveGeom.Colors = colors;
        }

        private void ButtonUpdateSineCurve_OnClick(object sender, RoutedEventArgs e)
        {
            UpdateSineCurve();
        }

        private void ButtonShowNodeId_OnClick(object sender, RoutedEventArgs e)
        {
            NodeIdModel.IsRendering = ButtonShowNodeId.IsChecked == true;
        }
    }
}